﻿// ----------------------------------------------------------------------------------------
// Author:                    Magaly Vargas y Joaquín López
// Company:                   ALBALIA INTERACTIVA S.L.
// Assembly version:          1.0.0.0
// Date:                      22/02/2010
// Time:                      12:00
// Solution Name:             OffInvoiceWordAddIn
// Solution Filename:         OffInvoiceWordAddIn
// Solution FullFilename:     C:\Desarrollo\OffInvoiceWordAddIn
// Project Name:              OffInvoiceWordAddIn
// Project Filename:          OffInvoiceWordAddIn.csproj
// Project FullFilename:      C:\Desarrollo\OffInvoiceWordAddIn\OffInvoiceWordAddIn.csproj
// Project Item Name:         Calculos.cs
// Project Item Filename:     Calculos.cs
// Project Item FullFilename: C:\Desarrollo\OffInvoiceWordAddIn\Calculos.cs
// Project Item Kind:         Código
// Purpose:                   
//Copyright © 2009 - 2010 Albalia Interactiva S.L. Reservados todos los derechos.
// ----------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Windows.Forms;

namespace OffInvoiceWordAddIn
{

    /// <summary>
    /// Clase encargado de realizar los calculos contables necesarios para la generación de la factura
    /// </summary>
    public class Calculos
    {

        #region "Fields"

        /// <summary>
        /// Campo con el formato de los valores numericos
        /// </summary>
        FieldFormats ff;

        /// <summary>
        /// Campo con la cultura para los valorenes numéricos, fechas y moneda
        /// </summary>
        CultureInfo culture;

        /// <summary>
        /// Campo con el formaro de numero especificado
        /// </summary>
        NumberFormatInfo num;

        #endregion

        #region "Contructors"

        /// <summary>
        /// Contructor
        /// </summary>
        /// <param name="ff">Formato de los valores numéricos</param>
        public Calculos(FieldFormats ff)
        {
            this.ff = ff;
            culture = ff.Culture;
            num = culture.NumberFormat;
        }

        #endregion

        #region Total Fields

        /// <summary>
        /// Campo de tipo double que almacena el importe total bruto
        /// </summary>
        public double totalGrossAmount = 0.0;

        /// <summary>
        /// Campo de tipo double que almacena el importe total de la base imponible sobre la que se aplican los descuentos
        /// </summary>
        public double generalDiscountBase = 0.0;

        /// <summary>
        /// Campo de tipo double que almacena el importe total de los descuentos
        /// </summary>
        public double totalGeneralDiscount = 0.0;

        /// <summary>
        /// Campo de tipo double que almacena el importe total bruto
        /// </summary>
        public double totalGrossAmountBeforeTaxes = 0.0;

        /// <summary>
        /// Campo de tipo double que almacena el importe total de los impuestos repercutidos
        /// </summary>
        public double totalTaxesOutputs = 0.0;

        /// <summary>
        /// Campo de tipo double que almacena el importe total de los impuestos retenidos
        /// </summary>
        public double totalTaxesWithHeld = 0.0;

        /// <summary>
        /// Campo de tipo double que almacena el importe total de la factura
        /// </summary>
        public double totalAmount = 0.0;

        /// <summary>
        /// Campo de tipo Taxes que almacena los impuestos repercutidos
        /// </summary>
        public Taxes taxes = new Taxes();

        /// <summary>
        /// Campo de tipo Taxes que almacena los impuestos retenidos
        /// </summary>
        public Taxes taxesw;

        #endregion

        #region Detail Fields


        /// <summary>
        /// Campo de tipo double que almacena el concepto o descripción de la linea
        /// </summary>
        public string concepto = string.Empty;

        /// <summary>
        /// Campo de tipo double que almacena las unidades de la linea
        /// </summary>
        public double units = 0.0;

        /// <summary>
        /// Campo de tipo double que almacena el precio unitario de la linea
        /// </summary>
        public double precio = 0.0;

        /// <summary>
        /// Campo de tipo double que almacena el descuento de la linea
        /// </summary>
        public double desc = 0.0;

        /// <summary>
        /// Campo de tipo double que almacena el importe del impuesto repercutido (IVA) de la linea
        /// </summary>
        public double iva = 0.0;

        /// <summary>
        /// Campo de tipo double que almacena el importe del impuesto retenido (IRPF) de la linea
        /// </summary>
        public double irpf = 0.0;

        /// <summary>
        /// Campo de tipo double que almacena el importe de la bae imponible de la linea
        /// </summary>
        public double baseimp = 0.0;

        /// <summary>
        /// Campo de tipo double que almacena el importe total de la linea
        /// </summary>
        public double ammount = 0.0;

        #endregion

        #region Discount Fields

        /// <summary>
        /// Campo de tipo double que almacena la razón del descuento general
        /// </summary>
        public string discountdesc = string.Empty;

        /// <summary>
        /// Campo de tipo double que almacena el porcentaje de descuento
        /// </summary>
        public double discRate = 0.0;

        /// <summary>
        /// Campo de tipo double que almacena el importe des descuento
        /// </summary>
        public double dicsammount = 0.0;

        #endregion

        #region "Public Methods"

        /// <summary>
        /// Recibe una matriz de 6 columnas con los valores de la tabla detalles
        /// El orden de los valores es: concepto, unidades, precio, descuento, iva, irpf,
        /// </summary>
        /// <param name="rows">Matriz con los valores de la tabla detalles</param>
        /// <returns>Una matriz con los valores calculados baseimponible e importe</returns>
        public object[,] computeDetails(object[,] rows)
        {

            object[,] results = new object[rows.GetLength(0), 11];

            try
            {
                for (int i = 0; i < rows.GetLength(0); i++)
                {
                    initDetailFields(rows, i);
                    double costo = Calculos.redondeo(units * precio);
                    baseimp = Calculos.redondeo(costo - (costo * desc / 100));
                    ammount = Calculos.redondeo(baseimp + (baseimp * iva / 100) - (baseimp * irpf / 100));
                    totalGrossAmount += Calculos.redondeo(baseimp);
                    results[i, 0] = rows[i, 0];
                    results[i, 1] = concepto;
                    results[i, 2] = units;
                    results[i, 3] = precio;
                    results[i, 4] = costo;
                    results[i, 5] = desc;
                    results[i, 6] = costo * desc / 100;
                    results[i, 7] = baseimp;
                    results[i, 8] = iva;
                    results[i, 9] = irpf;
                    results[i, 10] = ammount;

                    //if (iva > 0)
                    //{
                    taxes.BaseImponiblePrefix = string.Concat(Loader.LoadLabel(ff.messagePath, "InfoTaxablebaxe"), iva, "%", ")");
                    taxes.AmmountPrefix = string.Concat(Loader.LoadLabel(ff.messagePath, "Infotaxamount"), iva, ")");
                    taxes.setTaxes(iva.ToString(), baseimp * iva / 100, baseimp);
                    //}

                    if (irpf > 0)
                    {
                        if (taxesw == null)
                        {
                            taxesw = new Taxes();
                        }
                        taxesw.setTaxes(irpf.ToString(), baseimp * irpf / 100, baseimp);
                    }

                    totalTaxesOutputs = taxes.Totaltaxes;
                    if (taxesw != null)
                    {
                        totalTaxesWithHeld = taxesw.Totaltaxes;
                    }
                    totalGrossAmountBeforeTaxes = Calculos.redondeo(totalGrossAmount - totalGeneralDiscount);
                    totalAmount = totalGrossAmountBeforeTaxes + totalTaxesOutputs - totalTaxesWithHeld;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(Loader.LoadLabel(ff.messagePath, ex.Message), Loader.LoadLabel(ff.messagePath, "captionDetails"), MessageBoxButtons.OK, MessageBoxIcon.Stop, MessageBoxDefaultButton.Button1);
                throw ex;
            }

            return results;

        }

        /// <summary>
        /// Recibe una matriz de 3 columnas con los valores de la tabla descuentos
        /// El orden de los valores es: descripcion,descuento
        /// </summary>
        /// <param name="rows">Matriz con los valores de la tabla descuentos</param>
        /// <returns>Una matriz con los valores y el importe</returns>
        public object[,] computeDiscount(object[,] rows)
        {
            generalDiscountBase = 0.0;
            totalGeneralDiscount = 0.0;
            dicsammount = 0.0;
            totalGrossAmountBeforeTaxes = 0.0;

            object[,] results = new object[rows.GetLength(0), 3];

            try
            {
                for (int i = 0; i < rows.GetLength(0); i++)
                {
                    initDiscountFields(rows, i);
                    dicsammount = Calculos.redondeo((totalGrossAmount - totalGeneralDiscount) * discRate / 100);
                    totalGeneralDiscount += Calculos.redondeo(dicsammount);
                    results[i, 0] = discountdesc;
                    results[i, 1] = discRate;
                    results[i, 2] = dicsammount;
                }
                totalGrossAmountBeforeTaxes = Calculos.redondeo(totalGrossAmount - totalGeneralDiscount);
                totalAmount = Calculos.redondeo(totalGrossAmountBeforeTaxes + totalTaxesOutputs - totalTaxesWithHeld);
            }
            catch (Exception ex)
            {
                MessageBox.Show(Loader.LoadLabel(ff.messagePath, ex.Message), Loader.LoadLabel(ff.messagePath, "captionDiscounts"), MessageBoxButtons.OK, MessageBoxIcon.Stop, MessageBoxDefaultButton.Button1);
                throw ex;
            }
            return results;
        }

        /// <summary>
        /// Inicializa las variables para el calculo de los detalles
        /// </summary>
        /// <param name="rows">Array que contiene los valores de los campos de la tabla de detalles</param>
        /// <param name="i">Numemero de fila que se esta procesando</param>                      
        private void initDetailFields(object[,] rows, int i)
        {


            try
            {
                concepto = ff.txtDescription_Validating((string)rows[i, 1]);

                units = double.Parse(ff.txtUd_Validating((string)rows[i, 2]), num);

                precio = double.Parse(ff.txtPrice_Validating((string)rows[i, 3]), num);

                desc = double.Parse(ff.txtDiscount_Validating((string)rows[i, 4]), num);

                iva = double.Parse(ff.cbxIVA_Validating((string)rows[i, 5]), num);

                irpf = double.Parse(ff.txtIRPF_Validating((string)rows[i, 6]), num);
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }

        /// <summary>
        /// Inicializa las variables para el calculo de los descuentos
        /// </summary>
        /// <param name="rows">Array que contiene los valores de los campos de la tabla de descuentos</param>
        /// <param name="i">Numemero de fila que se esta procesando</param>               
        private void initDiscountFields(object[,] rows, int i)
        {
            try
            {
                discountdesc = ff.txtDescription_Validating((string)rows[i, 0]);
                discRate = double.Parse(ff.txtDiscount_Validating((string)rows[i, 1]), num);
            }
            catch (Exception ex)
            {
                throw ex;
            }




        }

        /// <summary>
        /// Inicializa los valores de los totales de montobruto y descuentos generales
        /// </summary>
        /// <param name="tgrossamount">Importe bruto</param>
        /// <param name="tgeneraldisc">Importe descuento general</param>
        private void initTotals(double tgrossamount, double tgeneraldisc)
        {
            this.totalGrossAmount = tgrossamount;
            this.generalDiscountBase = this.totalGrossAmount;
            this.totalGeneralDiscount = tgeneraldisc;


        }

        /// <summary>
        /// Actualiza los valores para los totales
        /// </summary>
        public void resetTotals()
        {
            totalGrossAmount = 0.0;
            generalDiscountBase = 0.0;
            totalGeneralDiscount = 0.0;
            totalTaxesOutputs = 0.0;
            totalTaxesWithHeld = 0.0;
            totalAmount = 0.0;
            concepto = string.Empty;
            units = 0.0;
            precio = 0.0;
            desc = 0.0;
            iva = 0.0;
            irpf = 0.0;
            baseimp = 0.0;
            ammount = 0.0;
            discountdesc = string.Empty;
            discRate = 0.0;
            dicsammount = 0.0;
            taxes.TaxesList = null;
            taxes.Totaltaxes = 0.0;
        }

        /// <summary>
        /// Realiza el redondeo a 2 decimales de un número
        /// </summary>
        /// <param name="numero">Numero a redondear</param>
        /// <returns>Numero redondeado</returns>
        public static double redondeo(double numero)
        {
            double multi = Math.Pow(10.0d, (double)2);

            if (numero < 0)
                return (-(long)((-numero * multi) + 0.5d)) / multi;
            else
                return ((long)((numero * multi) + 0.5d)) / multi;
        }

        #endregion
    }

    /// <summary>
    /// Clase que define la estructura de los impuestos
    /// </summary>
    public class Taxes
    {


        #region "Fields"

        /// <summary>
        /// Campo de tipo double con el importe del impuesto
        /// </summary>
        private double totaltaxes = 0.0;

        /// <summary>
        /// Campo de tipo string con el tipo de impuesto
        /// </summary>
        private string taxType = string.Empty;

        /// <summary>
        /// Campo de tipo string con el prefijo de impuesto
        /// </summary>
        private string ammountPrefix = "Cuota (";

        /// <summary>
        /// Campo de tipo string con el prefijo de la base imponible
        /// </summary>
        private string baseImponiblePrefix;

        /// <summary>
        /// Campo de tipo double con el valor de l abase imponible
        /// </summary>
        private double taxablebase = 0.0;

        /// <summary>
        /// Campo de tipo double con el importe del impuesto
        /// </summary>
        private double taxamount = 0.0;

        /// <summary>
        /// Campo con la lista de  impuestos
        /// </summary>
        private List<Taxes> taxesList;



        #endregion

        #region "Properties"

        /// <summary>
        /// Propiedad para el importe total del impuesto
        /// </summary>
        public double Totaltaxes
        {
            get { return totaltaxes; }
            set { totaltaxes = value; }
        }


        /// <summary>
        /// Propiedad para el tipo de impuesto
        /// </summary>
        public string TaxType
        {
            get { return taxType; }
            set { taxType = value; }
        }

        /// <summary>
        /// Propiedad para el prefijo del impuesto
        /// </summary>
        public string AmmountPrefix
        {
            get { return ammountPrefix; }
            set { ammountPrefix = value; }
        }

        /// <summary>
        /// Propiedad para el prefijo de la base imponible
        /// </summary>
        public string BaseImponiblePrefix
        {

            get { return baseImponiblePrefix; }
            set { baseImponiblePrefix = value; }
        }

        /// <summary>
        /// Propiedad para el importe de la base imponible
        /// </summary>
        public double Taxablebase
        {
            get { return taxablebase; }
            set { taxablebase += value; }
        }

        /// <summary>
        /// Propiedad para el importe total del impuesto
        /// </summary>
        public double Taxamount
        {
            get { return taxamount; }
            set { taxamount += value; }
        }

        /// <summary>
        /// Propiedad para la lista de impuestos
        /// </summary>
        public List<Taxes> TaxesList
        {
            get { return taxesList; }
            set { taxesList = value; }
        }

        #endregion

        #region "Public Methods"

        /// <summary>
        /// Método que agrega un impuesto a la lista de impuestos
        /// </summary>
        /// <param name="taxType">Tipo de impuesto</param>
        /// <param name="taxAmount">Importe</param>
        /// <param name="taxableBase">Base imponible</param>
        public void setTaxes(string taxType, double taxAmount, double taxableBase)
        {
            bool assigned = false;

            if (taxesList == null)
            {
                taxesList = new List<Taxes>();
            }


            foreach (Taxes tax in taxesList)
            {
                if (tax.taxType.ToLower().Equals(taxType.ToLower()))
                {
                    tax.Taxamount = taxAmount;
                    tax.Taxablebase = taxableBase;
                    assigned = true;
                }
            }

            if (!assigned)
            {
                Taxes tax = new Taxes();
                tax.TaxType = taxType;
                tax.Taxamount = taxAmount;
                tax.Taxablebase = taxableBase;
                tax.AmmountPrefix = taxType;
                tax.BaseImponiblePrefix = taxType;
                this.taxesList.Add(tax);
            }
            //this.Totaltaxes += Calculos.redondeo(taxAmount);
            this.Totaltaxes += taxAmount;


        }

        #endregion

    }
}
